﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Drawing;
using Tools.BaseX;
using ZOIL.DataBackend.BaseX.Properties;
using ZOIL.DataModel;

namespace ZOIL.DataBackend.BaseX
{
    [Export(typeof(CollectionConnectionBase))]
    public class BaseXCollectionConnection : CollectionConnectionBase
    {
        #region private

        private readonly DatabaseConfiguration _config;

        private BaseXCollection _collection;

        private bool _isLocalServerRunning = false;

        #endregion

        public BaseXCollectionConnection()
        {
            _config = new DatabaseConfiguration();
            LoadXQuery = string.Format("for $id in //{0} return zoil:getPairById($id)", _config.DatabaseIdInIDatabaseObject);
        }

        private string Host
        {
            get { return _config.Host; }
            set { _config.Host = value; }
        }

        private BaseXCollectionConnection UseHost(string host)
        {
            Host = host;
            return this;
        }

        private int Port
        {
            get { return _config.Port; }
            set { _config.Port = value; }
        }

        private BaseXCollectionConnection UsePort(int port)
        {
            Port = port;
            return this;
        }

        private string Username
        {
            get { return _config.Username; }
            set { _config.Username = value; }
        }

        private BaseXCollectionConnection UseUsername(string username)
        {
            Username = username;
            return this;
        }

        private string Password
        {
            get { return _config.Password; }
            set { _config.Password = value; }
        }

        private BaseXCollectionConnection UsePassword(string password)
        {
            Password = password;
            return this;
        }

        private string DatabaseName
        {
            get { return _config.DatabaseName; }
            set { _config.DatabaseName = value; }
        }

        private BaseXCollectionConnection UseDatabaseName(string databaseName)
        {
            DatabaseName = databaseName;
            return this;
        }

        private string EventName
        {
            get { return _config.EventName; }
            set { _config.EventName = value; }
        }

        private BaseXCollectionConnection UseEventName(string eventName)
        {
            EventName = eventName;
            return this;
        }

        private bool SetUseUpdIndex
        {
            get { return _config.UseUpdIndex; }
            set { _config.UseUpdIndex = value; }
        }

        private BaseXCollectionConnection UseUpdIndex()
        {
            SetUseUpdIndex = true;
            return this;
        }

        private BaseXCollectionConnection DontUseUpdIndex()
        {
            SetUseUpdIndex = false;
            return this;
        }

        private TimeSpan CommitToDatabaseThrottleInterval
        {
            get { return _config.CommitToDatabaseThrottleInterval; }
            set { _config.CommitToDatabaseThrottleInterval = value; }
        }

        private BaseXCollectionConnection UseCommitToDatabaseThrottleInterval(TimeSpan commitToDatabaseInterval)
        {
            CommitToDatabaseThrottleInterval = commitToDatabaseInterval;
            return this;
        }

        private Func<DatabaseObjectActionEventArgs, bool> AcceptRemotelyCreatedObjectCondition
        {
            get { return _config.AcceptRemotelyCreatedObjectCondition; }
            set { _config.AcceptRemotelyCreatedObjectCondition = value; }
        }

        private BaseXCollectionConnection UseAcceptRemotelyCreatedObjectCondition(Func<DatabaseObjectActionEventArgs, bool> acceptRemotelyCreatedObjectCondition)
        {
            AcceptRemotelyCreatedObjectCondition = acceptRemotelyCreatedObjectCondition;
            return this;
        }

        private string LoadXQuery { get; set; }

        private BaseXCollectionConnection UseLoadXQuery(string loadXQuery)
        {
            LoadXQuery = loadXQuery;
            return this;
        }

        private ICollectionConnection Clear()
        {
            try
            {
                using (var client = new BaseXClient(_config.Host, _config.Port, _config.Username, _config.Password))
                {
                    try
                    {
                        client.DropDatabase(_config.DatabaseName);
                    }
                    catch
                    {
                    }
                    try
                    {
                        client.DropEvent(_config.EventName);
                    }
                    catch
                    {
                    }
                }
            }
            catch
            {
            }
            return this;
        }

        private ICollectionConnection ClearIf(bool condition)
        {
            if (condition)
            {
                return Clear();
            }
            else
            {
                return this;
            }
        }

        #region Implementation of ICollectionConnection

        public override string FriendlyName
        {
            get { return "BaseX Database"; }
        }

        public override Bitmap Icon
        {
            get { return Resources.BaseXLogo; }
        }

        public override IPersistableCollection Connect(Dictionary<string, object> parameters)
        {
            // BaseX func need to be installed on server!!!

            UseHost((string)parameters["host"]);
            UsePort((int)parameters["port"]);
            UseUsername((string)parameters["username"]);
            UsePassword((string)parameters["password"]);
            UseDatabaseName((string)parameters["databaseName"]);
            UseEventName((string)parameters["eventName"]);

            if (parameters["useUpdIndex"] != null && (bool)parameters["useUpdIndex"])
                UseUpdIndex();

            if (parameters["useAcceptRemotelyCreatedObjectCondition"] != null &&
                (bool)parameters["useAcceptRemotelyCreatedObjectCondition"])
                UseAcceptRemotelyCreatedObjectCondition(args => true);

            UseLoadXQuery((string)parameters["useLoadXQuery"]);
            UseCommitToDatabaseThrottleInterval((TimeSpan)parameters["useCommitToDatabaseThrottleInterval"]);

            if (parameters["clearIf"] != null)
                ClearIf((bool)parameters["clearIf"]);

            _collection = new BaseXCollection(_config, LoadXQuery);

            return _collection;
        }

        public override IList<T> CreateList<T>()
        {
            return new List<T>();
        }

        public override ObservableCollection<T> CreateObservableCollection<T>()
        {
            return new ObservableCollection<T>();
        }

        public override bool CanStartLocalDatabase()
        {
            // Check if requirements are fullfilled such as Java SDK is installed properly, etc.
            return true;
        }

        public override void StartLocalDatabase(Dictionary<string, object> parameters)
        {
            var port = (int)parameters["port"];
            if (BaseXServer.IsRunning(port))
                throw new Exception(string.Format("A BaseX server is already running on port {0}", port));

            var eventPort = port + 1;
            if (parameters.ContainsKey("eventPort"))
                eventPort = (int)parameters["eventPort"];

            var interactive = false;
            if (parameters.ContainsKey("interactive"))
                interactive = (bool)parameters["interactive"];

            BaseXServer.Start(port, eventPort, interactive);

            var username = ((string)parameters["username"]);
            var password = ((string)parameters["password"]);

            // Install required functx module
            using (var session = new BaseXSession("localhost", port, username, password))
            {
                session.ExecuteCommand("REPO INSTALL http://files.basex.org/modules/expath/functx-1.0.xar");
            }

            _isLocalServerRunning = true;
        }

        public override void Dispose()
        {
            if (_collection != null)
                _collection.Dispose();

            if (_isLocalServerRunning)
                BaseXServer.Stop(_config.Port);
        }

        #endregion
    }
}